fix(kafka): resolve disk-removal deadlock during rolling upgrade - #254
Merged
dobrerazvan merged 7 commits intoAug 26, 2026
Merged
Conversation
When a broker pod is deleted during rolling upgrade and a disk removal is pending (GracefulDiskRemovalScheduled), the operator enters a deadlock: reconcileKafkaPvc blocks the entire reconcile with "Disk removal pending", preventing reconcileKafkaPod from recreating the missing pod. Meanwhile, Cruise Control cannot complete the disk removal because the broker isn't running. Fix: move runningBrokers map building before reconcileKafkaPvc and pass it in. Before returning the blocking error, check if any broker with pending disk removal has a missing pod. If so, allow the reconcile to proceed so the pod can be recreated. The disk removal check is re-evaluated on the next cycle once the broker is back up. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… add tests - Fix #1 (HIGH): Override now checks IsDiskRebalance() in addition to IsDiskRemoval(), closing the same deadlock vector for rebalance states - Fix #2 (LOW): Include mountPath in the bypass log message for consistency with other disk-removal log messages - Fix #3 (LOW): Add tests for rebalance-state deadlock bypass and for newly-marked-for-removal with missing pod Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Proposal, design, and task tracking for the fix. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
amuraru
force-pushed
the
fix-disk-removal-deadlock-during-rolling-upgrade
branch
from
August 24, 2026 08:34
c0c20d0 to
e21f0ab
Compare
|
Should we add an e2e test to simulate full PVC reconciliation at step N, N+1, in this pod missing condition? |
eduardagarici
approved these changes
Aug 26, 2026
A terminally-failed or paused Cruise Control disk operation
(GracefulDisk{Removal,Rebalance}CompletedWithError / Paused) was treated
as "in progress" via IsDiskRemovalRunning(), so reconcileKafkaPvc returned
CruiseControlTaskRunning indefinitely. With all broker pods present this
froze the whole reconcile — including config rollout / rolling upgrade to
healthy brokers — since reconcileKafkaPod runs downstream of the PVC block.
Observed on a live 3-broker cluster stuck in ClusterRollingUpgrading with
every removed disk in GracefulDiskRemovalCompletedWithError.
Relaxing the block is data-safe: log.dirs retention
(shouldKeepRemovedLogDirInConfig) and PVC mount retention both keep the
removed disk in place until removal is confirmed *succeeded*, independent
of this block. Cruise Control also does not hang on a dead broker — it
marks the intra-broker task DEAD and completes with error — so a stalled
state is genuinely terminal, not in-flight work a restart could disrupt.
- add CruiseControlVolumeState.IsDiskOperationStalled() (CompletedWithError
/Paused), mirroring IsDownscaleStalled
- in handleDiskRemoval, set waitForDiskRemovalToFinish only for non-stalled
IsDiskRemoval()/IsDiskRebalance() states; branch selection (PVC deletion,
state marking) is unchanged
- tests: removal CompletedWithError / Paused and rebalance CompletedWithError
with pod present now proceed; Running + pod present still blocks
Complements the existing missing-pod bypass: narrowing handles a stalled
task with pods up; the bypass handles a genuinely-running task whose broker
pod is missing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
azun
approved these changes
Aug 26, 2026
Add an e2e spec that applies a single manifest carrying both a read-only broker config change (log.retention.hours, forces a rolling restart) and a disk removal, then asserts the cluster reconciles correctly: the config change propagates to broker ConfigMaps, the removed disk drops out of log.dirs, Cruise Control goes quiescent, and the cluster returns to ClusterRunning (a deadlock would time out this wait). - config/samples/simplekafkacluster_1disk_configchange.yaml: 2disk sample reduced to one disk (removes /kafka-logs3) plus a changed readOnlyConfig - tests/e2e/test_config_change_with_disk_removal.go: testConfigChangeWithDiskRemoval and brokerConfigMapsContainProperty helper - wire the spec into the suite after testMultiDiskRemoval (chains 2->1 disk) Covers the combined-operation happy path end to end; the stalled-removal and missing-pod deadlocks the fix targets remain covered by the unit tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
added in 5ffc741 |
The added stalled-state cases pushed the table-driven test past the funlen limit (355 > 323). Annotate with //nolint:funlen, matching the existing convention for long table tests in this file. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dobrerazvan
deleted the
fix-disk-removal-deadlock-during-rolling-upgrade
branch
August 26, 2026 14:07
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Two related deadlocks left clusters stuck in ClusterRollingUpgrading indefinitely, because reconcileKafkaPvc aborts the entire reconcile with CruiseControlTaskRunning ("Disk removal pending") before reconcileKafkaPod — which creates/restarts pods — ever runs.
Fix
(1) Missing-pod bypass — build runningBrokers before reconcileKafkaPvc and pass it in; before returning the blocking error, if a broker with pending removal/rebalance has no pod, return nil so the pod can be recreated.
(2) Narrow the blocking wait — add CruiseControlVolumeState.IsDiskOperationStalled() (CompletedWithError / Paused, mirroring IsDownscaleStalled) and, in handleDiskRemoval, set waitForDiskRemovalToFinish only for genuinely-progressing states. Branch selection (PVC deletion, state marking) is unchanged.
The two are complementary.
Why relaxing the block is data-safe
The block is not what protects log.dirs integrity — two independent mechanisms already do, both keyed off success, not the desired spec:
So a ConfigOutOfSync broker can restart mid-removal with the disk still in log.dirs and mounted — no stranded data.
Verified against Cruise Control source: remove_disks runs as intra-broker replica movement; a dead broker causes CC to mark the task DEAD ("destination disk is down", Executor.java:2145-2151) and complete — it does not hang in execution — so a stalled state is genuinely terminal, not in-flight work a restart could disrupt.
Testing
Not in scope
Driving a CompletedWithError removal to actually retry/succeed (vs. just no longer blocking) — a follow-up in the CC task reconciler.
Type of Change
Checklist